/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2008，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	GWTRPCServlet.java
 * 模块说明：	
 * 修改历史：
 * Feb 13, 2008 - lxm - 创建。
 */
package com.hd123.rumba.request;

import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.hibernate.HibernateException;

import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.gwt.base.server.RBRemoteServiceServlet;
import com.hd123.rumba.runtime.HibernateSession;

/**
 * 所有GWT模块调用的统一处理Servlet。
 * <p>
 * 
 * 所谓GWT模块调用包括对public资源调用和rpc调用两个部分。
 * 
 * @author lxm
 * 
 */
public class GWTModuleServlet extends RemoteServiceServlet {

  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException, ServletException {
    try {
      doProcess(request, response, null);
    } catch (BusinessException e) {
      throw new ServletException(e);
    }
  }

  public String processCall(String payload) throws SerializationException {
    try {
      return doProcess(this.getThreadLocalRequest(), this.getThreadLocalResponse(), payload);
    } catch (BusinessException e) {
      throw new SerializationException(e);
    }
  }

  private String doProcess(HttpServletRequest request, HttpServletResponse response, String payload)
      throws BusinessException {
    // 解析Request
    RequestParts parts = new RequestParts(request);

    // 根据模块名找到*.gwt.xml，解析其中的servlet等信息。
    GWTModuleDef moduleDef = GWTModuleDefLoader.load(parts.moduleName);

    // 区分请求是rpc还是取得public中的内容。
    if (!moduleDef.getServlets().containsKey("/" + parts.partialPath)) {
      doGetPublicFile(parts.moduleName, parts.partialPath, moduleDef, response);
      return null;
    } else {
      return doGetService(payload, parts.moduleName, parts.partialPath, moduleDef);
    }

  }

  private void doGetPublicFile(String moduleName, String partialPath, GWTModuleDef moduleDef,
      HttpServletResponse response) throws BusinessException {
    assert moduleName != null;
    assert partialPath != null;
    assert moduleDef != null;

    StringBuffer sbPath = new StringBuffer();
    sbPath.append("/");
    if (!"".equals(GWTConstants.PREFIX)) {
      sbPath.append(GWTConstants.PREFIX);
      sbPath.append("/");
    }
    sbPath.append(moduleName);
    sbPath.append("/");
    sbPath.append(partialPath);

    String realPath = this.getServletContext().getRealPath(sbPath.toString());
    response.setContentType(this.getServletContext().getMimeType(realPath));
    try {
      ServletOutputStream out = response.getOutputStream();
      try {
        FileInputStream in = new FileInputStream(realPath);
        byte abyte0[] = new byte[2048];
        int bytesRead, len = 0;
        while (-1 != (bytesRead = in.read(abyte0, 0, abyte0.length))) {
          len += bytesRead;
          out.write(abyte0, 0, bytesRead);
        }
        response.setContentLength(len);
      } finally {
        out.flush();
      }
    } catch (IOException e) {
      if (e.getMessage() != null && e.getMessage().indexOf("Broken pipe") >= 0)
          return;
      if (e.toString().indexOf("Connection reset by peer: socket write error") >= 0)
          return;
      throw new BusinessException(e);
    }
  }

  private String doGetService(String payload, String moduleName, String servletPath,
      GWTModuleDef moduleDef) throws BusinessException {
    assert moduleName != null;
    assert servletPath != null;
    assert moduleDef != null;

    String className = moduleDef.getServlets().get("/" + servletPath);
    RemoteServiceServlet service;
    try {
      service = (RemoteServiceServlet) Class.forName(className).newInstance();
    } catch (InstantiationException e) {
      throw new BusinessException(e);
    } catch (IllegalAccessException e) {
      throw new BusinessException(e);
    } catch (ClassNotFoundException e) {
      throw new BusinessException(e);
    }

    if (service instanceof RBRemoteServiceServlet) {
      ((RBRemoteServiceServlet) service).setRequest(this.getThreadLocalRequest());
      ((RBRemoteServiceServlet) service).setResponse(this.getThreadLocalResponse());
    }

    try {
      RPCRequest rpcRequest = RPC.decodeRequest(payload, service.getClass(), this);
      return RPC.invokeAndEncodeResponse(service, rpcRequest.getMethod(), rpcRequest
          .getParameters(), rpcRequest.getSerializationPolicy());
    } catch (SerializationException e) {
      throw new BusinessException(e);
    } catch (IncompatibleRemoteServiceException e) {
      throw new BusinessException(e);
    } finally {
      try {
        HibernateSession.closeSession();
      } catch (HibernateException e) {
      }
    }
  }

  /** 从请求中取得有关信息。 */
  private static class RequestParts {
    /** 模块名 */
    public final String moduleName;
    /** 模块名之后的请求路径。 */
    public final String partialPath;

    public RequestParts(HttpServletRequest request) throws BusinessException {
      String pathInfo = request.getServletPath() + request.getPathInfo();

      String prefix = "/" + GWTConstants.PREFIX + "/";
      if (pathInfo.startsWith(prefix))
        pathInfo = pathInfo.substring(prefix.length() - 1);

      if (pathInfo != null) {
        int slash = pathInfo.indexOf('/', 1);
        if (slash != -1) {
          moduleName = pathInfo.substring(1, slash);
          partialPath = pathInfo.substring(slash + 1);
          return;
        } else {
          moduleName = pathInfo.substring(1);
          partialPath = "";
          return;
        }
      }
      throw new BusinessException("无法解释的请求：" + pathInfo);
    }
  }

}
